﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.ServiceModel;
using System.Text;
using System.Threading.Tasks;
using System.Web.Script.Services;
using Muel.TypeScriptFacade.Core.Configuration;

namespace Muel.TypeScriptFacade.Core
{
    public class TypeScriptServiceManager
    {
        private List<TypeScriptService> services = new List<TypeScriptService>();
        private TypeScriptObjectManager objectManager;
        private TypeScriptFacadeSettings settings;

        public TypeScriptServiceManager()
            : this(null) { }

        public TypeScriptServiceManager(TypeScriptFacadeSettings settings)
        {
            if (settings == null)
            {
                settings = TypeScriptFacadeSettings.Default;
            }
            this.settings = settings;
            this.objectManager = new TypeScriptObjectManager(this);
        }

        public TypeScriptFacadeSettings Settings
        {
            get { return settings; }
        }

        public void Load(Assembly assembly, IEnumerable<TypeScriptServiceUrlHint> serviceUrls)
        {
            services.AddRange(FindIn(assembly, serviceUrls));
        }

        public void Load(Assembly assembly)
        {
            Load(assembly, null);
        }

        internal IEnumerable<TypeScriptService> FindIn(Assembly assembly, IEnumerable<TypeScriptServiceUrlHint> serviceUrls)
        {
            // get all types in the assembly
            Type[] types = assembly.GetTypes();
            return types
                .Where(type => 
                    !type.IsInterface &&                    
                    (
                        (settings.ServiceProvider == ServiceProviderEnumeration.AspNetWebService && type.HasAttributeOfType<ScriptServiceAttribute>())
                        ||
                        (settings.ServiceProvider == ServiceProviderEnumeration.WcfWebService && type.HasAttributeOfType<ServiceContractAttribute>(true))
                    ) &&
                    !Settings.Exclusions.Contains(type.FullName))
                .Select(type =>
                    new TypeScriptService(
                        this,
                        type,
                        (serviceUrls == null) ? null : serviceUrls.FirstOrDefault(hint => hint.ServiceClassFullName.Equals(type.FullName))));
        }

        public void Load(IEnumerable<Assembly> assemblies)
        {
            foreach (Assembly assembly in assemblies)
            {
                Load(assembly, null);
            }
        }

        internal TypeScriptObjectManager ObjectManager { get { return objectManager; } }

        internal void EnsureInitialised()
        {
            ObjectManager.EnsureInitialised();
        }

        public TypeScriptService[] Services { get { return services.ToArray(); } }
        public TypeScriptType[] Types { get { return objectManager.GetObjects(); } }

        #region Messages

        public IEnumerable<TypeScriptFacadeMessage> GetMessages()
        {
            return this.Services
                .SelectMany(svc => svc.Methods)
                .SelectMany(m => m.Messages);
        }

        #endregion

        #region TypeScript generation

        public string ToTypeScript()
        {
            EnsureInitialised();

            string output = TypeScriptGenerator.Create(this);
            return output;
        }

        #endregion
    }
}
